package com.woniuxy.lab.woniu_lab.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.lab.woniu_lab.controller.form.CheckCodeForm;
import com.woniuxy.lab.woniu_lab.controller.form.GetCodeForm;
import com.woniuxy.lab.woniu_lab.controller.form.RegisterForm;
import com.woniuxy.lab.woniu_lab.exception.UserException;
import com.woniuxy.lab.woniu_lab.exception.WebException;
import com.woniuxy.lab.woniu_lab.exception.WebExceptionCode;
import com.woniuxy.lab.woniu_lab.model.Order;
import com.woniuxy.lab.woniu_lab.model.User;
import com.woniuxy.lab.woniu_lab.dao.UserDao;
import com.woniuxy.lab.woniu_lab.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.lab.woniu_lab.util.*;
import lombok.SneakyThrows;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RBucket;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.woniuxy.lab.woniu_lab.exception.WebExceptionCode.FREQUENT_LOGIN;
import static com.woniuxy.lab.woniu_lab.exception.WebExceptionCode.PHONE_IS_EXISTS;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Silas
 * @since 2023-03-15
 */
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {
    @Value("${aliOSS.EndPoint}")
    private String endPoint;// 地域节点
    @Value("${aliOSS.BucketURL}")
    private String bucketURL;// bucket公网域名
    @Value("${aliOSS.BucketName}")
    private String bucketName;// bucket名称
    @Value("${aliOSS.AccessKeyID}")
    private String accessKeyID;
    @Value("${aliOSS.AccessKeySecret}")
    private String accessKeySecret;



    //年费会员价格
    @Value("${vip-money.vip}")
    private  BigDecimal VIP_MONEY;
    //终身会员价格
    @Value("${vip-money.svip}")
    private BigDecimal SVIP_MONEY;

    /**
     * token标头
     */
    public static final String LAP_USER_TOKEN = "lap-user-token";

    @Resource
    private RedissonClient redisson;

    @Resource
    private RocketMQTemplate rocketMQTemplate;

    /**
     *  判断用户是否为空
     */
    public User assertUser() {
        Integer userId = null;
        try {
            userId = JwtUtil.getUserIdFromReq();
        } catch (Exception e) {
            throw new WebException(4001,"你还未登录,请登录再尝试");
        }
        User user = getById(userId);
        if (!user.getStatus().equals("ENABLE")){
            JwtUtil.rmToken();
            throw new WebException(4009,"由于你的违规操作,你当前状态为不可用");
        }
        if(user==null) throw new WebException(5010,"当前用户数据异常,请重新登录");
        return user;
    }

    @Override
    public String becomeVIP(String password) {
        User user = assertUser();
        if (user.getVip().equals("SVIP")) throw new WebException(400,"您已经是终身会员,无需办理该业务");
        if (!user.getPassword().equals(password)) throw new WebException(400,"支付密码错误");
        String message = "";
        //判断蜗牛币是否够
        if (user.getBalance().compareTo(VIP_MONEY) < 1)
            throw new WebException(400,"您当前蜗牛币不足以购买,请先充值后在购买,感谢您的支持");
        //如果是会员就直接续约
        if (user.getVip().equals("VIP") && user.getVipTime() != null && user.getVipTime().isAfter(LocalDateTime.now())){
            //会员到期时间加一年
            LocalDateTime localDateTime = user.getVipTime().plusYears(1);
            user.setVipTime(localDateTime);
            message="会员续约成功,过期时间为"+localDateTime.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
        }else {
            //获取当前时间 并加1年
            LocalDateTime localDateTime = LocalDateTime.now().plusYears(1);
            user.setVipTime(localDateTime);
            //修改用户vip状态
            user.setVip("VIP");
            message = "年费会员办理成功,过期时间为"+localDateTime.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
        }
        //扣钱
        user.setBalance(user.getBalance().subtract(VIP_MONEY));
        //加钱
        user.setTotalMoney(user.getTotalMoney().add(VIP_MONEY));

        //生成订单
        creatOrder(user,"VIP",-1);
        return message;
    }


    @Override
    public void becomeSVIP(String password) {
        User user = assertUser();
        if (user.getVip().equals("SVIP")) throw new WebException(400,"感谢您你支持!!你已是终身会员无需再次购买.");
        if (!user.getPassword().equals(password)) throw new WebException(400,"支付密码错误");
        //判断蜗牛币是否够
        if (user.getBalance().compareTo(SVIP_MONEY) < 1)
            throw new WebException(400,"您当前蜗牛币不足以购买,请先充值后在购买,感谢您的支持");

        //扣蜗牛币
        user.setBalance(user.getBalance().subtract(SVIP_MONEY));


        //加消费总金额
        user.setTotalMoney(user.getTotalMoney().add(SVIP_MONEY));
        //修改VIP状态
        user.setVip("SVIP");
        //设置过期时间为当前时间的300年后
        user.setVipTime(LocalDateTime.now().plusYears(500));
        //修改user
        //创建订单
        creatOrder(user,"SVIP",-2);

    }

    // 发送验证码
    @SneakyThrows
    @Override
    public String sendCode(GetCodeForm form) {
        RBucket<Integer> bucket = redisson.getBucket(WebUtil.getIp());
        Integer loginError = bucket.get();
        if (loginError == null || loginError < 7){
            String code = RandomUtil.randomNumbers(6).toUpperCase();
            this.saveCode(form.getPhone(), form.getSight(), code);
            System.out.println(code);
//        if (ProfileUtils.isProduction()) {
            SmsUtils.sendSms(code, form.getPhone());
//        }
            return code;
        }else {
            throw new WebException(5001,"因为你过多得登录错误,现在无法发送验证码");
        }
    }

    @Override
    public Boolean checkCode(CheckCodeForm form) {
        String phone = form.getPhone();
        String sight = form.getSight();
        String code = form.getCode();
        RMap<String, Map<String, String>> rmap = redisson.getMap(phone);
        if(rmap.isEmpty()) throw new WebException(WebExceptionCode.CODE_HAS_EXPIRE);
        Map<String, String> map = rmap.get(phone);
        if(map== null || map.isEmpty()) throw new WebException(WebExceptionCode.CODE_HAS_EXPIRE);
        String myCode = map.get(sight);
        if(myCode == null) throw new WebException(WebExceptionCode.CODE_HAS_EXPIRE);
        if(!myCode.equals(code)) throw new WebException(WebExceptionCode.CODE_IS_INCORRECT);
        return true;
    }

    // 用户注册
    @SneakyThrows
    @Override
    public void register(RegisterForm registerForm) {
        //手机号排重
        User phone = getOne(new QueryWrapper<User>().eq("phone", registerForm.getPhone()));
        if (ObjectUtil.isNotEmpty(phone)){
            throw new UserException(PHONE_IS_EXISTS);
        }
        User user = new User();
        String md5 = MD5Util.MD5(registerForm.getPassword());
        user.setPhone(registerForm.getPhone());
        user.setNickname(registerForm.getUsername());
        user.setPassword(md5);
        this.save(user);
    }

    /**
     *  密码登录
     */
    @SneakyThrows
    @Override
    public String loginByPwd(String phone, String password) {
        User user = this.getUserByPhone(phone);
        String dbPwd = user.getPassword();
        String md5 = MD5Util.MD5(password);
        //获取IP
        String ip = WebUtil.getIp();
        RBucket<Integer> bucket = redisson.getBucket(ip);

        //登录失败
        if (!dbPwd.equals(md5)){
            if(bucket==null || bucket.size()==0){
                bucket.set(1,2, TimeUnit.HOURS);
            }

            if (bucket.get() > 6){
                throw new WebException(FREQUENT_LOGIN);
            }

            //写入Redis定时2小时
            bucket.set(bucket.get()+1,2, TimeUnit.HOURS);
            //抛密码验证失败异常
            throw new WebException(WebExceptionCode.PWD_IS_INCORRECT);
        }

        // 发放token
        HashMap<String, Integer> map = new HashMap<>();
        map.put("userId", user.getId());
        String token = JwtUtil.getToken(map, 2);
        //验证通过，token写入请求头
        HttpServletResponse response = WebUtil.getResponse();
        response.addHeader(LAP_USER_TOKEN,token);

        if(bucket==null || bucket.size()==0){
            return token;
        }else if (bucket.get() > 6){
            throw new WebException(FREQUENT_LOGIN);
        }
        //登录成功，删除redis的记录
        bucket.delete();
        //登录成功
        return token;
    }


    @Override
    public String loginByCode(String phone, String code) {
        RBucket<Integer> bucket = redisson.getBucket(WebUtil.getIp());

        Integer loginErrorTime = bucket.get();

        if (loginErrorTime!=null && loginErrorTime>6){
            throw new WebException(1009,"登录错误过多,请稍后在尝试");
        } else {
            User user = this.getUserByPhone(phone);
            String sight = "login";
            RMap<String, Map<String, String>> rmap = redisson.getMap(phone);
            if(rmap.isEmpty()) {
                loginError(bucket, loginErrorTime);
                throw new WebException(WebExceptionCode.CODE_HAS_EXPIRE);// 验证码过期
            }
            Map<String, String> map = rmap.get(phone);
            if(map== null || map.isEmpty()) {
                loginError(bucket, loginErrorTime);
                throw new WebException(WebExceptionCode.CODE_HAS_EXPIRE);
            }
            String myCode = map.get(sight);
            if(myCode == null) {
                loginError(bucket, loginErrorTime);
                throw new WebException(WebExceptionCode.CODE_HAS_EXPIRE);
            }
            if(!myCode.equals(code)) {
                loginError(bucket, loginErrorTime);
                throw new WebException(WebExceptionCode.CODE_IS_INCORRECT);// 验证码有误
            }
            // 发放token
            HashMap<String, Integer> tmap = new HashMap<>();
            tmap.put("userId", user.getId());
            String token = JwtUtil.getToken(tmap, 2);
            HttpServletResponse response = WebUtil.getResponse();
            response.addHeader(LAP_USER_TOKEN, token);
            return token;
        }

    }

    private static void loginError(RBucket<Integer> bucket, Integer loginErrorTime) {
        if (loginErrorTime == null){
            bucket.set(1,30,TimeUnit.MINUTES);
        }else if (loginErrorTime == 6){
            bucket.set(7,2,TimeUnit.HOURS);
        }else {
            bucket.set(loginErrorTime +1,30,TimeUnit.MINUTES);
        }
    }

    // 重置密码
    @SneakyThrows
    @Override
    public void findMyPwd(String phone, String password) {
        User user = this.getUserByPhone(phone);
        String md5 = MD5Util.MD5(password);
        user.setPassword(md5);
        this.updateById(user);
    }

    // 修改密码
    @SneakyThrows
    @Override
    public void changePwd(Integer userId, String password, String newPassword) {
        User user = this.getById(userId);
        if(user == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        String md5 = MD5Util.MD5(password);
        if(!user.getPassword().equals(md5)) throw new WebException(WebExceptionCode.PWD_IS_INCORRECT);
        String newPwd = MD5Util.MD5(newPassword);
        user.setPassword(newPwd);
    }

    // 修改头像
    @SneakyThrows
    @Override
    public String updatePic(MultipartFile file) {
        User user = assertUser();
        if(user == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);

        String orderNo = new Snowflake(1, 1).nextIdStr();// 雪花算法生成随机字符串
        String rawName = file.getOriginalFilename();// 获取原始文件名称
        String fileName = orderNo + rawName;
        OSS ossClient = new OSSClientBuilder().build(endPoint, accessKeyID, accessKeySecret);// 创建OSS连接服务对象
        ossClient.putObject(bucketName, fileName, file.getInputStream());// 向bucketName这个桶容器存入名为rawName的文件（流形式上传）
        String fileUrl = bucketURL + fileName;// 文件的url
        // String fileUrl = OSSNewUtil.uploadFile(file);
        user.setPic(fileUrl);
        this.updateById(user);
        return fileUrl;
    }

    // 私有方法，向数据库中保存验证码
    private void saveCode(String phone, String code, String sight) {
        RMap<String, Map<String, String>> rmap = redisson.getMap(phone);
        if (!rmap.isEmpty()){
            throw new WebException(2001,"验证码已存在,验证码过期后在发送");
        }

        HashMap<String, String> map = new HashMap<>();
        map.put(code, sight);
        rmap.put(phone, map);

        rmap.expire(Duration.ofSeconds(3*60));// 设置3分钟过期
    }


    /**
     * 生成订单消息,并修改用户
     * @param user 用户
     * @param vipStr vip状态
     */
    private void creatOrder(User user,String vipStr,Integer vipId) {
        //修改用户
        updateById(user);

        Order order = new Order();
        //雪花算出订单编号
        Snowflake snowflake = new Snowflake(1,1);
        order.setNo(vipStr+snowflake.nextIdStr());
        order.setUserId(user.getId());
        order.setItemId(vipId);
        order.setInfo(vipStr);
        order.setCreateTime(LocalDateTime.now());
        order.setMoney(VIP_MONEY);
        //消息队列新增订单
        rocketMQTemplate.syncSend("add_order_topic",order);
    }

    private User getUserByPhone(String phone){
        User user = this.getOne(new QueryWrapper<User>().eq("phone", phone));
        if (user == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        if(!"ENABLE".equals(user.getStatus())) throw new WebException(WebExceptionCode.USER_IS_NOT_AVAILABLE);
        return user;
    }

}
